In [208]:
from vincent import *

Visualization

The top-most element of Vega JSON documents is implemented by the Visualization class. Visualizations contain all information for rendering a complete document.

We'll start by creating a new Visualization.


In [209]:
vis = Visualization(width=400, height=200)

The keywords width and height are Vega document keys. All valid Vega keys can be specified as keywords in the class constructors. The keys are implemented as class properties - complete with doc strings - and can be explored via help (and tab-completion in ipython).


In [210]:
help(Visualization)


Help on class Visualization in module vincent.visualization:

class Visualization(vincent.core.GrammarClass)
 |  Visualization container class.
 |  
 |  This class defines the full visualization. Calling its ``to_json``
 |  method should return a complete Vega definition.
 |  
 |  The sub-elements of the visualization are stored in the ``data``,
 |  ``axes``, ``marks``, and ``scales`` attributes. See the docs for each
 |  attribute for details.
 |  
 |  Method resolution order:
 |      Visualization
 |      vincent.core.GrammarClass
 |      __builtin__.object
 |  
 |  Methods defined here:
 |  
 |  __init__(self, *args, **kwargs)
 |      Initialize a Visualization
 |      
 |      In addition to setting any attributes, this sets the data, marks,
 |      scales, and axes properties to empty KeyedLists if they aren't
 |      defined by the arguments.
 |  
 |  axis_titles(self, x=None, y=None)
 |      Apply axis titles to the figure.
 |      
 |      This is a convenience method for manually modifying the "Axes" mark.
 |      
 |      Parameters
 |      ----------
 |      x: string, default 'null'
 |          X-axis title
 |      y: string, default 'null'
 |          Y-axis title
 |      
 |      Example
 |      -------
 |      >>>vis.axis_titles(y="Data 1", x="Data 2")
 |  
 |  colors(self, brew=None)
 |      Convenience method for adding color brewer scales to charts with a
 |      color scale, such as stacked or grouped bars.
 |      
 |      See the colors here: http://colorbrewer2.org/
 |      
 |      Or here: http://bl.ocks.org/mbostock/5577023
 |      
 |      This assumes that a 'color' scale exists on your chart.
 |      
 |      Parameters
 |      ----------
 |      brew: string, default None
 |          Color brewer scheme (BuGn, YlOrRd, etc)
 |  
 |  display(self)
 |      Display visualization inline in IPython notebook
 |  
 |  legend(self, title=None, scale='color')
 |      Convience method for adding a legend to the figure.
 |      
 |      Important: This defaults to the color scale that is generated with
 |      Line, Area, Stacked Line, etc charts. For bar charts, the scale ref is
 |      usually 'y'.
 |      
 |      Parameters
 |      ----------
 |      title: string, default None
 |          Legend Title
 |      scale: string, default 'color'
 |          Scale reference for legend
 |  
 |  validate(self, require_all=True, scale='colors')
 |      Validate the visualization contents.
 |      
 |      Parameters
 |      ----------
 |      require_all : boolean, default True
 |          If True (default), then all fields ``data``, ``scales``,
 |          ``axes``, and ``marks`` must be defined. The user is allowed to
 |          disable this if the intent is to define the elements
 |          client-side.
 |      
 |      If the contents of the visualization are not valid Vega, then a
 |      :class:`ValidationError` is raised.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  axes
 |      list or KeyedList of ``Axis`` : Axis definitions
 |      
 |      Axes define the locations of the data being mapped by the scales.
 |      See the :class:`Axis` class for details.
 |  
 |  data
 |      list or KeyedList of ``Data`` : Data definitions
 |      
 |      This defines the data being visualized. See the :class:`Data` class
 |      for details.
 |  
 |  height
 |      int : Height of the visualization in pixels
 |      
 |      Default is 500 if undefined.
 |  
 |  legends
 |      list or KeyedList of ``Legends`` : Legend definitions
 |      
 |      Legends visualize scales, and take one or more scales as their input.
 |      They can be customized via a LegendProperty object.
 |  
 |  marks
 |      list or KeyedList of ``Mark`` : Mark definitions
 |      
 |      Marks are the visual objects (such as lines, bars, etc.) that
 |      represent the data in the visualization space. See the :class:`Mark`
 |      class for details.
 |  
 |  name
 |      string : Name of the visualization (optional)
 |  
 |  padding
 |      int or dict : Padding around visualization
 |      
 |      The padding defines the distance between the edge of the
 |      visualization canvas to the visualization box. It does not count as
 |      part of the visualization width/height. Values cannot be negative.
 |      
 |      If a dict, padding must have all keys ``''top'``, ``'left'``,
 |      ``'right'``, and ``'bottom'`` with int values.
 |  
 |  scales
 |      list or KeyedList of ``Scale`` : Scale definitions
 |      
 |      Scales map the data from the domain of the data to some
 |      visualization space (such as an x-axis). See the :class:`Scale`
 |      class for details.
 |  
 |  viewport
 |      2-element list of ints : Dimensions of the viewport
 |      
 |      The viewport is a bounding box containing the visualization. If the
 |      dimensions of the visualization are larger than the viewport, then
 |      the visualization will be scrollable.
 |      
 |      If undefined, then the full visualization is shown.
 |  
 |  width
 |      int : Width of the visualization in pixels
 |      
 |      Default is 500 if undefined.
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from vincent.core.GrammarClass:
 |  
 |  from_json(self)
 |      Load object from JSON
 |      
 |      Not yet implemented.
 |  
 |  to_json(self, path=None, html_out=False, html_path='vega_template.html', validate=False, pretty_print=True)
 |      Convert object to JSON
 |      
 |      Parameters
 |      ----------
 |      path: string, default None
 |          Path to write JSON out. If there is no path provided, JSON
 |          will be returned as a string to the console.
 |      html_out: boolean, default False
 |          If True, vincent will output an simple HTML scaffold to
 |          visualize the vega json output.
 |      html_path: string, default 'vega_template.html'
 |          Path for the html file (if html_out=True)
 |      validate : boolean
 |          If True, call the object's `validate` method before
 |          serializing. Default is False.
 |      pretty_print : boolean
 |          If True (default), JSON is printed in more-readable form with
 |          indentation and spaces.
 |      
 |      Returns
 |      -------
 |      string
 |          JSON serialization of the class's grammar properties.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from vincent.core.GrammarClass:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

This will already dump some JSON, but there isn't enough yet to define a valid Vega document:


In [211]:
print(vis.to_json())


{
  "axes": [],
  "data": [],
  "height": 200,
  "legends": [],
  "marks": [],
  "scales": [],
  "width": 400
}

Vincent only creates JSON data for keys that have been explicitly defined. For example, though the property padding already exists,


In [212]:
hasattr(vis, 'padding')


Out[212]:
True

it doesn't show up in the JSON output until it's been assigned. Similarly, Vega properties that have been assigned can also be deleted, which will prevent them from appearing in the encoded JSON. For example,


In [213]:
del vis.width
print(vis.to_json())


{
  "axes": [],
  "data": [],
  "height": 200,
  "legends": [],
  "marks": [],
  "scales": []
}

has removed "width" from the returned JSON. We'd like our width defined though, so let's put it back.


In [214]:
vis.width = 400

We can add margins around the edges by setting the padding property to a 4-element dict:


In [215]:
vis.padding = {'top': 10, 'left': 30, 'bottom': 20, 'right': 10}
print(vis.to_json())


{
  "axes": [],
  "data": [],
  "height": 200,
  "legends": [],
  "marks": [],
  "padding": {
    "bottom": 20,
    "left": 30,
    "right": 10,
    "top": 10
  },
  "scales": [],
  "width": 400
}

Because valid JSON does not make valid Vega, Vincent tries to prevent us from generating documents that won't render figures in the client (usually the browser). For example, setting the padding property with an invalid 'above' key raises a ValueError.


In [216]:
try:
    vis.padding = {'above': 10, 'left': 30, 'bottom': 20, 'right': 10}
except ValueError as e:
    print(e)


Padding must have keys "top", "left", "right", "bottom".

Vincent always raises ValueError if it thinks a property assignment isn't valid Vega. Otherwise we might spend quite some time debugging our documents in the client.

Next we'll look at defining document data.

Data

Data in Vega is defined in tabular form. Though it's certainly possible to set the data manually, it's much easier to use the class methods Data.from_iters, Data.from_pandas, and Data.from_numpy.


In [217]:
mydata = Data.from_mult_iters(x=['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'], y=[28, 55, 43, 91, 81, 53, 19, 87, 52], idx='x')
print(mydata.to_json(pretty_print=False))


{"name": "table", "values": [{"col": "y", "idx": "A", "val": 28}, {"col": "y", "idx": "B", "val": 55}, {"col": "y", "idx": "C", "val": 43}, {"col": "y", "idx": "D", "val": 91}, {"col": "y", "idx": "E", "val": 81}, {"col": "y", "idx": "F", "val": 53}, {"col": "y", "idx": "G", "val": 19}, {"col": "y", "idx": "H", "val": 87}, {"col": "y", "idx": "I", "val": 52}]}

Note that in Vega, all data must have a name. Vincent will set the data name to 'table' if it isn't provided.

Let's add this data to our visualization:


In [218]:
vis.data.append(mydata)

The data in the Vega JSON document is an array, allowing us to define multiple data sets in one document. The data elements (as well as scales and axes) can be accessed by their location,


In [219]:
vis.data[0].values[0]


Out[219]:
{'col': 'y', 'idx': 'A', 'val': 28}

or by their name,


In [220]:
vis.data['table'].values[0]


Out[220]:
{'col': 'y', 'idx': 'A', 'val': 28}

Assignments by name are also possible.


In [221]:
vis.data['table'] = mydata

In this case the key of the data ('table') must match the name property of the assigned object, or a ValidationError is raised. Data can also point to an external source by setting the url property. See the Vega documentation for more info.

Let's add some scales to our visualization.

Scales

Scales map the data from the space of the data (the domain of the scale) to the space of the visualization (the range of the scale). These are defined using the Scale class.


In [222]:
vis.scales['x'] = Scale(name='x', type='ordinal', range='width')

The domain of the scales is usually defined by referencing a field of the data. These reference are defined by the DataRef class.


In [223]:
vis.scales['x'].domain = DataRef(data='table', field='data.idx')
vis.scales['x'].to_json(pretty_print=False)


Out[223]:
'{"domain": {"data": "table", "field": "data.idx"}, "name": "x", "range": "width", "type": "ordinal"}'

Our x-data is ordinal (since it's letters), but our y-data is quantitative. If the type property is undefined, Vega defaults to a linear type.


In [224]:
vis.scales.append(Scale(name='y', range='height', nice=True, domain=DataRef(data='table', field='data.val')))

Now let's add some axes to define the visualization space.

Axes

Axes provide a guide for translating spatial relationships about data. They are defined, naturally, by the Axis class.


In [225]:
vis.axes.extend([Axis(type='x', scale='x'), Axis(type='y', scale='y')])

While Vega generally doesn't care if we label our data 'x' or 'y' (as opposed to, say, 'time'), the axes are the exception. Here, 'x' is always used to refer to the horizontal axis, while 'y' refers to the vertical axis. Also note that while the data and scales properties are keyed according to the name property of their contents, the axes property is keyed according to the type property.

Marks

Marks are the most fundamental part of the visualization; they're what the viewer sees. All marks in Vega have a type, such as line, rect, ect. To add the bars to our bar chart, we add rect marks:


In [226]:
bars = Mark(type='rect')

We define the data set the Marks represent by assigning the from_ property to a MarkRef:


In [227]:
bars.from_ = MarkRef(data='table')

Though from_ is used for the property name - from would be invalid Python syntax - the JSON field is still "from":


In [228]:
bars.to_json(pretty_print=False)


Out[228]:
'{"from": {"data": "table"}, "type": "rect"}'

The appearance of the marks are determined by the properties property. The properties is set to a MarkProperties object that has enter, exit, hover, and update, which correspond to different events that may alter the mark's appearance. Each property of the MarkProperties object can be set to a PropertySet class that specifies the appearance details, such as color, stroke, etc.. Finally, each property of PropertySet is set to a ValueRef class. The ValueRefs link the details of the mark's appearance to the data via the scales.

This might sound a bit complicated, because it is. Maintaining the flexibility of something like a visualization grammar requires multiple levels of abstraction.


In [229]:
bars.properties = MarkProperties()
bars.properties.enter = PropertySet()

# Set the x-location of the bars to the data's x field, mapped through the x scale. Internally vincent tidies the data to 'idx'
bars.properties.enter.x = ValueRef(scale='x', field='data.idx')
# Use "band" to set the width of the bars to be flush against one another, minus a 1-pixel offset.
bars.properties.enter.width = ValueRef(scale='x', band=True, offset=-1)
# Set the height of the bars to the data's y field, mapped through the y scale.Internally vincent tidies the data to 'val'
bars.properties.enter.y = ValueRef(scale='y', field='data.val')
# Set the bottom of the bars to the x-axis.
bars.properties.enter.y2 = ValueRef(scale='y', value=0)

bars.properties.update = PropertySet()
# On the "update" event (see the Vega docs for details), set the color of the bars to "steelblue".
bars.properties.update.fill = ValueRef(value='steelblue')
# On the "hover" event, set the color of the bars to "red".
bars.properties.hover = PropertySet()
bars.properties.hover.fill = ValueRef(value='red')

As one might imagine, some fairly complex, dynamic marks can be defined. Use your imagination.

Now adding the marks to the visualization,


In [230]:
vis.marks.append(bars)

Finally, visualizing it in the Notebook:


In [231]:
import vincent
vincent.core.initialize_notebook()
vis.display()


Once the visualization is defined, it's fairly easy to change it's properties:


In [234]:
vis.marks[0].properties.hover.fill.value = 'gray'
vis.width = 800
vis.display()